/*
 * 导致arrayMethods 对象
 */

import { def } from '../util/index'

// 缓存 Array.prototype
const arrayProto = Array.prototype
// 实现 arrayMethods.__proto__ === Array.prototype
// 数组本身也是对象，那么必然能访问到其 __proto__ 属性，此时的arrayMethods为一个对象，但其__proto__指向了数组原型。
export const arrayMethods = Object.create(arrayProto)

// 数组的这些方法会改变数组，因此在观察数组时需拦截数据的这些方法。
// 拦截原理为：这些方法调用的查找链为：本身实例属性方法 -->原型的属性方法 --> 原型的父类的属性方法
// 通过在arrayMethods这个对象上创建一些与数组原型链上同名的方法，可以在数组调用这些方法时，依据js对象的原型链调用顺序来
// 保证实例属性上的同名方法会被优先调用。
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * 为数组实例增加同名方法
 */
methodsToPatch.forEach(function (method) {
  // 先缓存数组原本的变异方法
  const original = arrayProto[method]
  // 后使用 def 函数在 arrayMethods 上定义与数组变异方法同名的函数，在函数体内优先调用了缓存下来的数组变异方法
  def(arrayMethods, method, function mutator (...args) {
    // 将数组原本变异方法的返回值赋值给 result 常量
    const result = original.apply(this, args)
    // 定义 ob 常量，它是 this.__ob__ 的引用，其中this 是数组实例本身
    const ob = this.__ob__
    // 定义 inserted 变量，这个变量用来保存那些被新添加进来的数组元素
    let inserted
    switch (method) {
      // 数组添加新元素方法
      case 'push':
      case 'unshift':
        inserted = args
        break
      // splice 函数从第三个参数开始到最后一个参数都是数组的新增元素，所以直接从第三个参数截取到最后，最后获取到数组的新增元素
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 当存在新增元素时，调用 observeArray 函数对其进行观测
    if (inserted) ob.observeArray(inserted)
    // 在数组实例上添加变异方法，实际上是修改了数组，所以要将该数组的所有依赖(观察者)全部拿出来执行
    ob.dep.notify()
    // 保证拦截函数的功能与数组原本变异方法的功能是一致的
    return result
  })
});


